PWM
For controlling the DC motor and servo motor. DC motor: power (speed) Servo motor: angle
Pulse Width Modulation(PWM) - We'll take a look into the total amount of on-off time of a signal. And utilize this data, rather than just the current state, to convey useful information.
REF: ELEC1100 lecture 10 (2022, Deparment of Electronic and Computer Engineering, HKUST)


- 1.The output frequency
- 2.The on-time (The time of "HIGH voltage")
- (Duty Cycle) which is on-time to period ratio
- in Second

The data-sheet will usually provide the frequency and on-time (pulse) to use. The SG90 servo uses 50 Hz and 1-2ms on-time.
We will also use PWM signal to control a DC motor, which duty cycle implies the output power of the DC motor. For example letting the motor to spin at a lower speed.
Using Timers. (MCU_Clock)
Frequency of clock (MCU_Clock):
The timer of clock (in our board we are using 84MHz)
Prescaler value(PSC):
- to scale down the frequency of the clock
- a 16-bit unsigned integer
Auto-reloaded counter(ARR):
- to control the output signal
- a 16-bit unsigned integer
Frequency of PWM:
The output (or desired frequency) for the motors We are using 50Hz for this servo motor.

Items in red are what we are concerned about
As you can see, when there are 2 peaks in the MCU_Clock, 1 peak in Clock after Prescaler is generated. So the prescaler value must be 2 right? No! As we are programmers, we always count from 0. So, the Prescaler value = 1.
The auto-reload counter in the above example is 36
As you can see, when there are 37 peaks in clock after prescaler, the auto-reload counter increases by 1, when the value is > 36, 1 peak in Counter overflow is generated and the counter is reset to 0. Again, we are programmers, so the auto-reload counter = 36.
On the above picture you can see that the prescaler value and auto-reload counter help reduce the frequency of the MCU_Clock and generate a lower frequency.
The purpose of having both of them is that our MCU runs at a high frequency. If we were to work with servos (assume they require 50Hz) and use only the Prescaler or only the Auto-Reload, we won't be able to reduce to the targeted frequency. That's why we need both.
The difference between the two is that Prescaler value is just aiming to reduce the frequency, while auto-reload counter also aims as a counter.
The prescaler value and Auto-reload counter are limited to a 16-bit unsigned integer only. Thus, the maximum value of both values is 2^16 - 1 = 65535.
After all, how do we get the output frequency???
If we need a frequency output of 50Hz, what are the 3 possible combinations of prescaler value and auto-reload value? (Given that the clock frequency is 84MHz)❓
Duty cycle:
According to the figure below, the number on the left-hand side is the duty cycle, it means the percentage of time that a signal is given as "high"(or 5V). i.e.,

By only looking at the picture, you may not understand what is happening. It maybe easier for us - programmers to understand through actual code.
#include <stdbool.h>
#include <stdio.h>
int main() {
int ARR = 1000; // Auto-reloaded counter
int CCRx = 250; // Compare Register
bool is_high;
for (int i = 0; i < ARR; i++) {
if (i < CCRx) {
is_high = true;
} else {
is_high = false;
}
// Or prefered shorthand
// is_high = (i < CCRx);
}
}
CCRx means Compare Register. x means the number of channels that we are using.When the auto-reload counter is smaller than
CCRx, the output PWM will give a HIGH value.The ARR (auto-reload counter) from the previous paragraph is used as a downscaler of clock frequency. But in here, it acts as a denominator and
CCRx acts as an numerator.
From the formula:
There are many combinations of prescaler value and auto-reload counter that can generate the same frequency output. How can we choose a better value?
As you may notice, the ARR acts as an denominator in the Duty Cycle formula, therefore if we want to output a short on-time, we need to have larger denominator. Notice that both of the CCR and ARR has to be an 16-bit unsign integer.
As a result, Larger Auto-reload Value and Smaller Prescaler Value would be better when outputing a short on-time.
If we need a frequency output of 50Hz and on-time of 0.5ms, what are the possible combinations of prescaler value, auto-reload value and compare value? (Given that the clock frequency is 84MHz)❓
Each pin can use specific timers and timer channels. You can check the configuration of the board from CubeMX to find out which timer and channel to use.
- Each timer will only have one prescaler value and auto-reload counter.
- Each timer will have several channels that can output different on-time(compare value). (CCR1, CCR2 ...)
Which means those different channels will share same prescaler value and auto-reload counter, but have different on-time. So when using motor with different freqency, you may need different timer.
There are 4 steps in setting up the PWM output channel and the pin to use.
- 1.In catergories, click Timers then choose the timer you want to use.Setup the Mode same as the figure shown. It's fine if you only get 1 channel.
- 2.Set the Parameter Settings same as the figure shown
Supposedly, You don't have to change anything

3. IMPORTANT: Enable the global interrupt of the timer.

- 4.Assign the GPIO pin to be the specific timer and channel. e.g Assign the PC7 pin to output the pwm signal of TIM3_CH2.

There are 4 steps in coding:
- 1.Initialize the Timer for PWM
This should be implemented in the beginning ofmain.c
MX_TIM1_Init();
.
.
.
MX_TIM8_Init();
- 2.Set the Prescaler value, Auto-reload counter
//in tutorial3_pwm.c in pwm_init()
TIM1->ARR = 1234; //set the timer1 auto-reload counter
TIM1->PSC = 5678; //set the timer1 prescaler value
//We are using timer 5 channel 1!!!
Hint: The clock of the board is running at around 84MHz.
- 3.Start the Timer (in
tutorial3_pwm.cinpwm_init())
//in tutorial3_pwm.c in pwm_init()
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
//HAL_TIM_PWM_Start(timer, channel);
//htim1 refers to timer 1
//We are using timer 5 channel 1!!!
- 4.Change the CCR as required for the classwork/homework
TIM1->CCR1 = 321; //set the compare value of timer1 channel1
TIM1->CCR2 = 678; //set the compare value of timer1 channel2
//add the below in main.c
...
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* add the following 3 lines*/
void pwm_init(void); //add this line!
void pwm_classwork(void); //add this line!
void pwm_homework(void); //add this line!
/* USER CODE END PFP */
...
pwm_init(); //add this line!
while (1) {
pwm_classwork(); //add this line!
}
...
Try to control servo motor to turn to -90 degrees-> 0 degrees -> 90 degrees (with a short pause at 0 degreesNote: for the servo motor we are using, the on-time at -90 degrees should be 0.5ms, and the ontime at 90 degrees should be 2.5ms. Calculate the on-time for 0 degrees on your own.We are using TIM5 and channel 1Bonus: control the angle of the motor with a button
Last modified 3d ago
